
from pytest import mark, raises
from unittest.mock import patch, Mock

from dotfilebackup.settings import ZERO_HOUR
from dotfilebackup.parser import (
    InterpolationError, KeyInterpolationHandler, datetime_interpolation_handler)


foo = {
    'name'       : 'foo',
    'parser_key' : True,
    'updatable'  : True,
    'val'        : 'foo',
    'format'     : None,
    'ignore'     : None,
    'forbidden'  : None,
}
bar = {
    'name'       : 'bar',
    'parser_key' : True,
    'updatable'  : False,
    'val'        : 'bar',
    'format'     : None,
    'ignore'     : None,
    'forbidden'  : None,
}
baz = {
    'name'       : 'baz',
    'parser_key' : False,
    'updatable'  : False,
    'val'        : 'baz',
    'format'     : None,
    'ignore'     : None,
    'forbidden'  : None,
}
ban = {
    'name'       : 'ban',
    'parser_key' : False,
    'updatable'  : True,
    'val'        : None,
    'format'     : 'ban{foo}',
    'ignore'     : True,
    'forbidden'  : None,
}
foobar = {
    'name'       : 'foobar',
    'parser_key' : True,
    'updatable'  : True,
    'val'        : None,
    'format'     : '{foo}{bar}',
    'ignore'     : None,
    'forbidden'  : None,
}
x = {
    'name'       : 'x',
    'parser_key' : True,
    'updatable'  : True,
    'val'        : 'x',
    'format'     : None,
    'ignore'     : None,
    'forbidden'  : None,
}
y = {
    'name'       : 'y',
    'parser_key' : True,
    'updatable'  : True,
    'val'        : 'y',
    'format'     : None,
    'ignore'     : None,
    'forbidden'  : None,
}
foobaz = {
    'name'       : 'foobaz',
    'parser_key' : False,
    'updatable'  : True,
    'val'        : 'FOOBAZ',
    'format'     : '{foo}{baz}',
    'ignore'     : ['x', 'y'],
    'forbidden'  : ['a', 'b'],
}
foobarbaz = {
    'name'       : 'foobarbaz',
    'parser_key' : 'triple',
    'updatable'  : True,
    'val'        : None,
    'format'     : '{foo}{bar}{baz}',
    'ignore'     : None,
    'forbidden'  : None,
}


class TestKeyInterpolationHandler:
    def test_init_parser_keys(self):
        i = [foo, bar, baz, foobar, foobarbaz]
        ih = KeyInterpolationHandler(i)
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
            'foobar': 'foobar',
            'triple': 'foobarbaz',
        }

    def test_init_passed_keys_are_available_to_get(self):
        ih_empty = KeyInterpolationHandler()
        assert ih_empty.get('foo') == None
        ih_foo = KeyInterpolationHandler([foo])
        assert ih_foo.get('foo') == foo

    def test_new_creates_instance_with_independent_data(self):
        ih_old = KeyInterpolationHandler([foo])
        ih_new = ih_old.new()
        assert ih_old is not ih_new
        assert ih_old.get('foo')['val'] == 'foo'
        assert ih_new.get('foo')['val'] == 'foo'
        old_foo = ih_old.get('foo')
        old_foo['val'] = 'FOO'
        ih_old.set(old_foo)
        assert ih_old.get('foo')['val'] == 'FOO'
        assert ih_new.get('foo')['val'] == 'foo'
        new_foo = ih_new.get('foo')
        new_foo['val'] = 'Foo'
        ih_new.set(new_foo)
        assert ih_old.get('foo')['val'] == 'FOO'
        assert ih_new.get('foo')['val'] == 'Foo'

    def test_new_creates_instance_with_updated_keys_updatable_or_not(self):
        foo_2 = {
            'name'       : 'foo',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foo',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        bar_2 = {
            'name'       : 'bar',
            'parser_key' : True,
            'updatable'  : False,
            'val'        : 'bar',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        ih_old = KeyInterpolationHandler([foo, bar])
        ih_new = ih_old.new([foo_2, bar_2])
        assert ih_old.get('foo') == foo
        assert ih_old.get('bar') == bar
        assert ih_new.get('foo') == foo_2
        assert ih_new.get('bar') == bar_2

    def test_set_new_updatable_key(self):
        ih = KeyInterpolationHandler()
        assert ih.get('foo') == None
        ih.set(foo)
        assert ih.get('foo') == foo

    def test_set_old_updatable_key(self):
        ih = KeyInterpolationHandler([foo])
        assert ih.get('foo') == foo
        new_foo = ih.get('foo')
        new_foo['val'] = 'FOO'
        ih.set(new_foo)
        assert ih.get('foo') == new_foo

    def test_set_new_non_updatable_key(self):
        ih = KeyInterpolationHandler()
        assert ih.get('bar') == None
        ih.set(bar)
        assert ih.get('bar') == bar

    def test_set_old_non_updatable_key(self):
        ih = KeyInterpolationHandler([bar])
        assert ih.get('bar') == bar
        new_bar = ih.get('bar')
        new_bar['val'] = 'BAR'
        with raises(InterpolationError) as e: ih.set(new_bar)
        assert f"'bar' interpolation is non-updatable" in str(e.value)

    def test_set_new_update_true_parser_key(self):
        ih = KeyInterpolationHandler()
        assert ih.parser_keys == {}
        assert ih.get('foo') == None
        ih.set(foo)
        assert ih.parser_keys == {'foo': 'foo'}
        assert ih.get('foo') == foo

    def test_set_new_update_false_parser_key(self):
        ih = KeyInterpolationHandler()
        assert ih.parser_keys == {}
        assert ih.get('baz') == None
        ih.set(baz)
        assert ih.parser_keys == {}
        assert ih.get('baz') == baz

    def test_set_new_update_str_parser_key(self):
        ih = KeyInterpolationHandler([foo, bar, baz])
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
        }
        ih.set(foobarbaz)
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }

    def test_set_old_update_true_parser_key(self):
        ih = KeyInterpolationHandler([foo])
        assert ih.parser_keys == {'foo': 'foo'}
        assert ih.get('foo')['val']== 'foo'
        new_foo = ih.get('foo')
        new_foo['val'] = 'FOO'
        ih.set(new_foo)
        assert ih.parser_keys == {'foo': 'foo'}
        assert ih.get('foo')['val']== 'FOO'

    def test_set_old_update_false_parser_key(self):
        ih = KeyInterpolationHandler([foo, ban])
        assert ih.parser_keys == {'foo': 'foo'}
        assert ih.get('ban')['format']== 'ban{foo}'
        new_ban = ih.get('ban')
        new_ban['format'] = 'BAN{foo}'
        ih.set(new_ban)
        assert ih.parser_keys == {'foo': 'foo'}
        assert ih.get('ban')['format']== 'BAN{foo}'

    def test_set_old_update_str_parser_key(self):
        ih = KeyInterpolationHandler([foo, bar, baz, foobarbaz])
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }
        assert ih.get('foobarbaz')['val']== 'foobarbaz'
        new_foobarbaz = ih.get('foobarbaz')
        new_foobarbaz['format'] = 'FOOBARBAZ'
        ih.set(new_foobarbaz)
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }
        assert ih.get('foobarbaz')['format']== 'FOOBARBAZ'

    def test_set_update_changes_parse_key(self):
        ih = KeyInterpolationHandler([foo, bar, baz, foobarbaz])
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }
        new_foo = ih.get('foo')
        new_foo['parser_key'] = False
        ih.set(new_foo)
        assert ih.parser_keys == {
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }
        new_foo_again = ih.get('foo')
        new_foo_again['parser_key'] = 'FOO'
        ih.set(new_foo_again)
        assert ih.parser_keys == {
            'FOO'   : 'foo',
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }
        new_foo_yet_again = ih.get('foo')
        new_foo_yet_again['parser_key'] = True
        ih.set(new_foo_yet_again)
        assert ih.parser_keys == {
            'foo'   : 'foo',
            'bar'   : 'bar',
            'triple': 'foobarbaz',
        }

    def test_set_name_absent_form_item(self):
        foom = foo.copy()
        del foom['name']
        ih = KeyInterpolationHandler()
        with raises(InterpolationError) as e: ih.set(foom)
        assert "???[name] is missing" in str(e.value)

    def test_get_present_key(self):
        ih = KeyInterpolationHandler([foo])
        assert ih.get('foo') == foo

    def test_get_missing_key(self):
        ih = KeyInterpolationHandler()
        assert ih.get('non-existent') == None

    def test_get_default_arg(self):
        ih = KeyInterpolationHandler()
        assert ih.get('non-existent', 'foo') == 'foo'
        assert ih.get('non-existent', {'foo':123}) == {'foo':123}

    def test_get_formats_val_if_format_is_defined(self):
        assert foobar['val'] == None
        ih = KeyInterpolationHandler([foo, bar, foobar])
        assert ih.get('foobar')['val'] == 'foobar'

    def test_get_ignores_value_of_val_and_sets_formated_value_form_format(self):
        assert foobaz['val'] == 'FOOBAZ'
        ih = KeyInterpolationHandler([x, y, foo, baz, foobaz])
        assert ih.get('foobaz')['val'] == 'foobaz'

    def test_get_retutns_independent_data(self):
        ih = KeyInterpolationHandler([foo])
        new_foo = ih.get('foo')
        new_foo['val'] = 'FOO'
        assert foo['val'] == 'foo'
        assert new_foo['val'] == 'FOO'

    def test_beware_of_changing_original_references(self):
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        old_ih = KeyInterpolationHandler([foom])
        new_ih = old_ih.new()
        assert old_ih.get('foom')['val'] == 'foom'
        assert new_ih.get('foom')['val'] == 'foom'
        foom['val'] = 'FOOM'
        assert old_ih.get('foom')['val'] == 'FOOM'
        assert new_ih.get('foom')['val'] == 'FOOM'

    def test_validation_in_init_handle_calls(self):
        with patch(__name__ + '.KeyInterpolationHandler._handle') as m_handle:
            _ = KeyInterpolationHandler()
            m_handle.assert_not_called()
        with patch(__name__ + '.KeyInterpolationHandler._handle') as m_handle:
            _ = KeyInterpolationHandler([foo])
            m_handle.assert_called_once_with([foo])

    def test_validation_in_new_handle_calls(self):
        ih = KeyInterpolationHandler([foo])
        with patch(__name__ + '.KeyInterpolationHandler._handle') as m_handle:
            _ = ih.new()
            m_handle.assert_not_called()
        with patch(__name__ + '.KeyInterpolationHandler._handle') as m_handle:
            # import pudb; pudb.set_trace()
            _ = ih.new([bar, baz])
            m_handle.assert_called_once_with([bar, baz])

    def test_validation_in_set_handle_calls(self):
        ih = KeyInterpolationHandler([foo])
        with patch(__name__ + '.KeyInterpolationHandler._handle') as m_handle:
            ih.set(bar)
            m_handle.assert_called_once_with([bar])

    def test_validate_in_handle_all_keys_are_present(self):
        ih = KeyInterpolationHandler()
        foom = {}
        foon = {
            'name'       : 'foon',
            # 'parser_key'
            'updatable'  : True,
            'val'        : None,
            'format'     : '{foo}{foom}',
            # 'ignore'
            'forbidden'  : 'no',
        }
        with raises(InterpolationError) as e: ih._handle([foom, foon])
        # import pudb; pudb.set_trace()
        assert "???[name] is missing" in str(e.value)
        assert "???[parser_key] is missing" in str(e.value)
        assert "???[updatable] is missing" in str(e.value)
        assert "???[val] is missing" in str(e.value)
        assert "???[format] is missing" in str(e.value)
        assert "???[ignore] is missing" in str(e.value)
        assert "???[forbidden] is missing" in str(e.value)
        assert "'foon'[parser_key] is missing" in str(e.value)
        assert "'foon'[ignore] is missing" in str(e.value)

    def test_validate_in_handle_name_type_str(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : None,
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert '???[name] is NoneType expected str' in str(e.value)

    def test_validate_in_handle_name_must_not_be_empty(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : '',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert '???[name] is empty expected something anything' in str(e.value)

    def test_validate_in_handle_name_must_be_unique(self):
        ih = KeyInterpolationHandler()
        foo_two = {
            'name'       : 'foo',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foo',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foo, foo_two])
        assert "???[name]=='foo' already present" in str(e.value)

    def test_validate_in_handle_parser_key_type_str_or_bool(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : 123,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        i = [foo, bar, baz, foobarbaz, foom]
        with raises(InterpolationError) as e: ih._handle(i)
        assert "'foom'[parser_key] is int expected str|bool" in str(e.value)

    def test_validate_in_handle_parser_key_non_empty_str(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : '',
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[parser_key] is empty expected something anything" in str(e.value)

    def test_validate_in_handle_parser_key_must_not_exist_in_interpolations(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : 'foo',
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foo, foom])
        assert "'foom'[parser_key]=='foo' in use by 'foo'" in str(e.value)

    def test_validate_in_handle_updatable_type_bool(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : 123,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[updatable] is int expected bool" in str(e.value)

    def test_validate_in_handle_val_type_str_or_none(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : [1, 2, 3],
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        i = [foo, bar, foobar, foom]
        with raises(InterpolationError) as e: ih._handle(i)
        assert "'foom'[val] is list expected str|None" in str(e.value)

    def test_validate_in_handle_format_type_str_or_none(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : {1, 2, 3},
            'ignore'     : None,
            'forbidden'  : None,
        }
        i = [foo, bar, foobar, foom]
        with raises(InterpolationError) as e: ih._handle(i)
        assert "'foom'[format] is set expected str|None" in str(e.value)

    def test_validate_in_handle_format_keys_are_available_interpolations(self):
        ih = KeyInterpolationHandler()
        with raises(InterpolationError) as e: ih._handle([foobar])
        assert "'foobar'[format]=='...{foo}...' not available" in str(e.value)
        assert "'foobar'[format]=='...{bar}...' not available" in str(e.value)

    def test_validate_in_handle_format_cannot_refer_to_it(self):  #:-D
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : '',
            'format'     : '{foom}',
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[format]=='...{foom}...' self-reference" in str(e.value)

    def test_validate_in_handle_val_empty_format_empty(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : '',
            'format'     : '',
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[format] is empty expected something anything" in str(e.value)

    def test_validate_in_handle_val_empty_format_none(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : '',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[val] is empty expected something anything" in str(e.value)

    def test_validate_in_handle_val_none_format_empty(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : None,
            'format'     : '',
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[format] is empty expected something anything" in str(e.value)

    def test_validate_in_handle_val_none_format_none(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : 123,
            'val'        : None,
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        err_msg = ("'foom'[val]==None and 'foom'[format]==None"
                   " expected ONE to be something anything")
        assert err_msg in str(e.value)

    def test_validate_in_handle_val_defined_format_empty(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : '',
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[format] is empty expected something anything" in str(e.value)

    def test_validate_in_handle_val_empty_format_defined(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : '',
            'format'     : 'foom',
            'ignore'     : None,
            'forbidden'  : None,
        }
        ih._handle([foom])  # valid situation expected no error

    def test_validate_in_handle_ignore_type_list_true_or_none(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : {1, 2, 3},
            'forbidden'  : None,
        }
        foon = {
            'name'       : 'foon',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foon',
            'format'     : None,
            'ignore'     : False,
            'forbidden'  : None,
        }
        i = [foo, baz, foobaz, ban, foom, foon]
        with raises(InterpolationError) as e: ih._handle(i)
        assert "'foom'[ignore] is set expected list[str]|True|None" in str(e.value)
        assert "'foon'[ignore]==False expected list[str]|True|None" in str(e.value)

    #
    # TODO: ponder wether is it necesarry for ignores to be avalable as even
    #       if someone adds '...{non_existent}...' to ignores and it will be
    #       replaced by parser with '{non_existent}' it's value will fail the
    #       'format' all keys have to be available interpolations validation
    #
    def test_validate_in_handle_ignore_keys_aviailable_as_interpolations(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : ['foo', 'x', 'y'],
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([foo, foom])
        assert "'foom'[ignore]==[... 'x', ...] not available" in str(e.value)
        assert "'foom'[ignore]==[... 'y', ...] not available" in str(e.value)

    def test_validate_in_handle_forbidden_type_list_or_none(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : 'foom',
            'format'     : None,
            'ignore'     : None,
            'forbidden'  : {1:11, 2:22},
        }
        i = [foo, ban, foom]
        with raises(InterpolationError) as e: ih._handle(i)
        assert "'foom'[forbidden] is dict expected list[str]|None" in str(e.value)

    def test_validate_in_handle_forbidden_not_allowed_in_format(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : 'foom',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : None,
            'format'     : '{x}{y}',
            'ignore'     : None,
            'forbidden'  : ['x', 'y'],
        }
        with raises(InterpolationError) as e: ih._handle([foom])
        assert "'foom'[format]=='...{x}...' forbidden" in str(e.value)
        assert "'foom'[format]=='...{y}...' forbidden" in str(e.value)

    def test_validate_in_handle_multiple_items(self):
        ih = KeyInterpolationHandler()
        foom = {
            'name'       : None,
            'parser_key' : False,
            'updatable'  : 0,
            'val'        : None,
            'format'     : 'foom',
            'ignore'     : False,
            'forbidden'  : None,
        }
        foon = {
            'name'       : 'foon',
            'parser_key' : 123,
            'updatable'  : 0,
            'val'        : None,
            'format'     : '',
            'ignore'     : False,
            'forbidden'  : 'no',
        }
        with raises(InterpolationError) as e: ih._handle([foom, foon])
        assert "???[name] is NoneType expected str" in str(e.value)
        assert "???[updatable] is int expected bool" in str(e.value)
        assert "???[ignore]==False expected list[str]|True|None" in str(e.value)
        assert "'foon'[parser_key] is int expected str|bool" in str(e.value)
        assert "'foon'[format] is empty expected something anything" in str(e.value)
        assert "'foon'[forbidden] is str expected list[str]|None" in str(e.value)

    def test_validate_in_handle_infinite_recursion(self):
        ih = KeyInterpolationHandler()
        x = {
            'name'       : 'x',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : None,
            'format'     : '{y}',
            'ignore'     : None,
            'forbidden'  : None,
        }
        y = {
            'name'       : 'y',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : None,
            'format'     : '{z}',
            'ignore'     : None,
            'forbidden'  : None,
        }
        z = {
            'name'       : 'z',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : None,
            'format'     : '{x}',
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([x, y, z])
        assert "'x'[format]=='{y}' infinite recursion" in str(e.value)
        assert "'y'[format]=='{z}' infinite recursion" in str(e.value)
        assert "'z'[format]=='{x}' infinite recursion" in str(e.value)

    @mark.parametrize('NAME', (
        'left_curly', 'l', 'right_curly', 'r', 'colon', 'c',
        'hash', 'h', 'space', 's', 'hyphen', 'minus', 'm'
    ))
    def test_validate_in_handle_do_not_use_base_interpolation_names(self, NAME):
        ih = KeyInterpolationHandler()
        base_name = {
            'name'       : f'{NAME}',
            'parser_key' : True,
            'updatable'  : True,
            'val'        : None,
            'format'     : 'anything',
            'ignore'     : None,
            'forbidden'  : None,
        }
        with raises(InterpolationError) as e: ih._handle([base_name])
        assert (f"???[name]=='{NAME}' in use by base interpolation") in str(e.value)


class TestDateTimeInterpolationHandler:
    @mark.parametrize(
        'KEY, VAL', (
        ('hour',            ZERO_HOUR.HOUR),
        ('min',             ZERO_HOUR.MIN),
        ('sec',             ZERO_HOUR.SEC),
        ('day',             ZERO_HOUR.DAY),
        ('timestamp',       ZERO_HOUR.TIMESTAMP),
        ('day_name',        ZERO_HOUR.DAY_NAME),
        ('day_short',       ZERO_HOUR.DAY_SHORT),
        ('weekday',         ZERO_HOUR.WEEKDAY),
        ('month',           ZERO_HOUR.MONTH),
        ('month_name',      ZERO_HOUR.MONTH_NAME),
        ('month_short',     ZERO_HOUR.MONTH_SHORT),
        ('year',            ZERO_HOUR.YEAR),
        ('year_short',      ZERO_HOUR.YEAR_SHORT),
        ('date',            f'{ZERO_HOUR.DAY}.{ZERO_HOUR.MONTH}.{ZERO_HOUR.YEAR}'),
        ('time',            f'{ZERO_HOUR.HOUR}-{ZERO_HOUR.MIN}-{ZERO_HOUR.SEC}'),
        ('datetime',        (f'{ZERO_HOUR.DAY}.{ZERO_HOUR.MONTH}.{ZERO_HOUR.YEAR}_'
                             f'{ZERO_HOUR.HOUR}-{ZERO_HOUR.MIN}-{ZERO_HOUR.SEC}')),
        )
    )
    def test_date_time_keys_return_expected_val(self, KEY, VAL):
        assert datetime_interpolation_handler.get(KEY)['val'] == VAL

#
